﻿#include "common.h" // // 包含预编译头文件
#include "MusicPlayer.h"
#include <stdexcept>

// 初始化全局变量
float TEMPO = 1.0f;
float PITCH = 1.0f;
float RATE = 1.0f;
int al_get_error = 0;
bool is_ALC_EXT_EFX_supported = false;

MusicPlayer::MusicPlayer(const std::string& filename)
    : my_ffmpeg(new MusicReader(filename))
    //, frame_list(std::vector<AVFrame*>(2))
    , is_playing(false)
    , is_seeking(false)
{
    if (open_file_error) {
        qDebug() << "Failed to open file.";
        delete my_ffmpeg;
        my_ffmpeg = nullptr;
        return;
    }
    else if (is_pcm_format_set_successfully)
        init_player();
    else 
        qDebug() << "Failed to set PCM format.";
}

MusicPlayer::~MusicPlayer()
{
    // 暂停 OpenAL 播放
    if (is_playing)
        pause();
    if (al_source) {
        alSourcei(al_source, AL_BUFFER, 0);
        alDeleteSources(1, &al_source);
    }
    if (al_context) {
        alcDestroyContext(al_context);
        alcMakeContextCurrent(nullptr);
    }
    if (al_device) {
        alcCloseDevice(al_device);
    }
    alDeleteBuffers(NUM_BUFFERS, al_buffers);
    //alDeleteFilters(1, &al_lowpass_filter);
    //alDeleteFilters(1, &al_highpass_filter);
    //alDeleteEffects(0, &al_reverb_effect);

    // 释放 ffmpeg
    if (my_ffmpeg) {
        delete my_ffmpeg;
        my_ffmpeg = nullptr;
    }

    // 删除 SoundTouch 对象
    //delete soundTouch;
}

void MusicPlayer::init_player()
{
    init_SoundTouch();

    init_OpenAL();
}

const double MusicPlayer::get_reader_duration() const
{
    return my_ffmpeg->get_duration();
}

void MusicPlayer::set_audio_tempo(float tempo)
{
    if (tempo >= 0.5f && tempo <= 5.0f) {
        soundTouch.setTempo(TEMPO = tempo);
    }
    else {
        qDebug() << "tempo must be greater than or equal to 0.5 and less than or equal to 5.0";
    }
}

void MusicPlayer::resave()
{
    my_ffmpeg->save_resampled_audio(); // 无用，暂时不用
}

void MusicPlayer::play()
{
    if (!my_ffmpeg->p_format_context ||
        !my_ffmpeg->p_codec_context ||
        !my_ffmpeg->p_audio_stream)
        throw std::runtime_error("File not opened");

    if (is_playing)
        return;
    pause(); // ����ͣ���ţ���ֹ�����̳߳�ͻ
    is_playing = true;
    // ��������߳�
    decode_thread = std::thread(&MusicPlayer::queue_SoundTouch_convert_frame, this);
    // ��������߳�
    play_thread = std::thread(&MusicPlayer::play_thread_func, this);
}

void MusicPlayer::pause()
{
    is_playing = false;

    // 等待解码线程结束
    if (decode_thread.joinable())
        decode_thread.join();

    // 暂停播放线程
    if (play_thread.joinable())
        play_thread.join();

    // 释放音频队列和 OpenAL 资源
    //frame_list.clear();
    //cleanup_al_buffer(al_source);
}

void MusicPlayer::reset_p_swr_context_output(AVChannelLayout out_layout, int out_sample_rate, AVSampleFormat out_sample_fmt)
{
    bool play_or_pause = is_playing;
    pause();
    my_ffmpeg->init_filters(out_layout);

    my_ffmpeg->reset_audio_sampler_output(out_layout);
    if (play_or_pause)
        play();
}

void MusicPlayer::play_thread_func()
{
    while (is_playing)
    {
        process_playback_OpenAL();
        std::this_thread::sleep_for(std::chrono::milliseconds(5));
    }
}

// 解码线程函数，转换音频帧并通过 SoundTouch 处理，然后将处理后的帧放入 frame_list
void MusicPlayer::queue_SoundTouch_convert_frame()
{
    while (is_playing) {
        if (frame_list.size() < 3) {
            AVFrame* next_frame = my_ffmpeg->next_audio_out_frame();
            //AVFrame* queued_frame = my_ffmpeg->convert_filters_frame(next_frame);
            // Qt 信号 emit audio_frame_read
            // int64_t pos_us = queued_frame->pts;
            // emit audio_frame_read(pos_us);

            if (!next_frame) {
                qDebug() << "No more audio frames to queue.";
                is_playing = false;
                emit audio_end();
                return;
            }
            /*av_frame_unref(next_frame);
            if (next_frame) {
                av_frame_free(&next_frame);
                next_frame = nullptr;
            }*/

            if (is_seeking)
                soundTouch.setTempo(3.0f);

            std::lock_guard<std::mutex> lock(player_mutex);
            // SoundTouch 仅支持浮点格式音频帧
            if (next_frame->format == AV_SAMPLE_FMT_FLT ||
                next_frame->format == AV_SAMPLE_FMT_FLTP) {
                int channels = next_frame->ch_layout.nb_channels;
                int samples = next_frame->nb_samples;
                const float* pcm_data = (float*)next_frame->data[0];
                std::vector<float> st_out(samples * channels);
                // 处理浮点格式音频帧时，直接使用 SoundTouch
                soundTouch.putSamples(pcm_data, samples);
                int nReceived = soundTouch.receiveSamples(st_out.data(), samples);
                if (nReceived > 0) {
                    AVFrame* st_frame = av_frame_alloc();
                    st_frame->format = next_frame->format;
                    st_frame->ch_layout = next_frame->ch_layout;
                    st_frame->sample_rate = next_frame->sample_rate;
                    st_frame->nb_samples = nReceived;
                    if (av_frame_get_buffer(st_frame, 0) < 0) {
                        qDebug() << "av_frame_get_buffer failed (float)";
                        av_frame_free(&st_frame);
                        av_frame_free(&next_frame);
                        soundTouch.setTempo(TEMPO);
                        return;
                    }
                    memcpy(st_frame->data[0], st_out.data(), nReceived * channels * sizeof(float));

                    // 转换滤波器处理音频帧，结果可能为空
                    AVFrame* filtered_frame = my_ffmpeg->convert_filters_frame(st_frame);
                    // 释放 SoundTouch 音频帧
                    av_frame_free(&st_frame);

                    AVFrame* s16_frame = av_frame_alloc();
                    s16_frame->format = AV_SAMPLE_FMT_S16;
                    s16_frame->ch_layout = next_frame->ch_layout;
                    s16_frame->sample_rate = next_frame->sample_rate;
                    s16_frame->nb_samples = nReceived;
                    s16_frame->pts = next_frame->pts;
                    if (av_frame_get_buffer(s16_frame, 0) < 0) {
                        qDebug() << "av_frame_get_buffer failed (s16)";
                        //av_frame_free(&st_frame);
                        av_frame_free(&filtered_frame);
                        av_frame_free(&s16_frame);
                        av_frame_unref(next_frame);
                        next_frame = nullptr;
                        soundTouch.setTempo(TEMPO);
                        return;
                    }
                    int ret = swr_convert_frame(my_ffmpeg->p_swr_context_output, s16_frame, filtered_frame);
                    if (ret < 0) {
                        qDebug() << "swr_convert_frame failed";
                        //av_frame_free(&st_frame);
                        av_frame_free(&filtered_frame);
                        av_frame_free(&s16_frame);
                        av_frame_free(&next_frame);
                        soundTouch.setTempo(TEMPO);
                        emit audio_error("swr_convert_frame failed");
                        is_playing = false;
                        return;
                    }
                    //av_frame_free(&st_frame);
                    av_frame_free(&filtered_frame);
                    av_frame_free(&next_frame);
                    frame_list.emplace_back(s16_frame);
                }
                else {
                    //qDebug() << "SoundTouch buffer underrun.";
                }
                soundTouch.setTempo(TEMPO);
            }
            else if (next_frame->format == AV_SAMPLE_FMT_S16) {
                // 队列音频帧
                frame_list.emplace_back(next_frame);
                soundTouch.setTempo(TEMPO);
            }
        }
        else   // 解码超前播放，休息一下下
            std::this_thread::sleep_for(std::chrono::milliseconds(2));
    }
}

void MusicPlayer::queue_audio_frame_to_OpenAL(AVFrame*& al_queue_frame)
{
    while (is_playing) {
        /*f (frame_list.size() < 2) {
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
            continue;
        }*/
        if (!al_queue_frame)
            return;

        std::lock_guard<std::mutex> lock(player_mutex);
        int channels = al_queue_frame->ch_layout.nb_channels;
        ALenum al_format = 0;
        int sample_size = 0;
        if (al_queue_frame->format == AV_SAMPLE_FMT_FLT) {
            al_format = get_openal_format<float>(channels);
            sample_size = sizeof(float);
        }
        else if (al_queue_frame->format == AV_SAMPLE_FMT_S16) {
            al_format = get_openal_format<int16_t>(channels);
            sample_size = sizeof(int16_t);
        }
        else {
            qDebug() << "Unsupported sample format:" << al_queue_frame->format;
            if (al_queue_frame) {
                av_frame_unref(al_queue_frame);
                al_queue_frame = nullptr;
            }
            return;
        }
        if (!al_format) {
            qDebug() << "Unsupported channel count:" << channels;
            return;
        }

        /*if (channels != 1)
            qDebug() << "Warning: Only mono source supports 3D spatialization in OpenAL.";*/

        static int buffer_index = 0;

        // 检查是否还有可用的缓冲区
        ALint buffers_queued;
        alGetSourcei(al_source, AL_BUFFERS_QUEUED, &buffers_queued);
        if (buffers_queued >= NUM_BUFFERS) {
            qDebug() << "No available buffers to queue audio frame.";
            std::this_thread::sleep_for(std::chrono::milliseconds(5));
            continue;
        }

        // 确保分配的 OpenAL 缓冲区数量为 NUM_BUFFERS
        ALuint bufferID = al_buffers[buffer_index];
        buffer_index = (buffer_index + 1) % NUM_BUFFERS;
        if (buffer_index < NUM_BUFFERS) {
            alBufferData(bufferID, al_format,
                al_queue_frame->data[0],
                al_queue_frame->nb_samples * channels * sample_size,
                al_queue_frame->sample_rate);
            alSourceQueueBuffers(al_source, 1, &bufferID);
        }
        // 处理完数据时，释放数据，置为 nullptr
        /*int sample_rate = al_queue_frame->sample_rate;
        int nb_samples = al_queue_frame->nb_samples;
        if (sample_rate > 0 && nb_samples > 0) {
            int ms = static_cast<int>(1000.0 * nb_samples / sample_rate);
            if (ms > 0) std::this_thread::sleep_for(std::chrono::milliseconds(ms));
        }*/
        if (al_queue_frame) {
            av_frame_unref(al_queue_frame);
            al_queue_frame = nullptr;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

// 播放线程，处理播放线程中，获取并释放播放完成的音频帧数据
void MusicPlayer::process_playback_OpenAL()
{
    // 查询当前队列中的缓冲区数量
    ALint processed = 0;
    alGetSourcei(al_source, AL_BUFFERS_PROCESSED, &processed);
    while (processed-- > 0) {
        ALuint buf_id = 0;
        alSourceUnqueueBuffers(al_source, 1, &buf_id);
        alGetError(); // Clear error code
        //al_debug("Error unqueuing buffer")
    }
    // 查询当前队列中的缓冲区数量
    ALint queued = 0;
    alGetSourcei(al_source, AL_BUFFERS_QUEUED, &queued);
    // 检查是否还有可用的缓冲区以供 OpenAL 使用
    if (queued++ < NUM_BUFFERS && !frame_list.empty() && frame_list.front()) {
        // 设置音频帧的时间戳，转换为当前帧的 pts
        AVFrame* al_frame = frame_list.front();
        if (al_frame) {
            int64_t ts = al_frame->pts - NUM_BUFFERS * al_frame->nb_samples;

            // 获取为时间戳
            //AVRational time_base = my_ffmpeg->p_audio_stream->time_base;
            
            // 转化为妙
            double pts_pos = (ts > 0 ? ts : 0) * av_q2d(my_ffmpeg->p_audio_stream->time_base);
            
            emit audio_frame_read(pts_pos * 1000); // 发送毫秒
        }
        queue_audio_frame_to_OpenAL(al_frame);
        frame_list.erase(frame_list.begin());
    }
    
    // 检查音频源的状态，如果为空闲并且队列中有缓冲区，则开始播放
    ALint state = 0;
    alGetSourcei(al_source, AL_SOURCE_STATE, &state);
    if (state != AL_PLAYING && queued > 0) {
        alSourcePlay(al_source);
    }
}

void MusicPlayer::seeking(int64_t pos_us)
{
    is_seeking = true;
    my_ffmpeg->seek(pos_us);
    is_seeking = false;
}

void MusicPlayer::seek_to_position(int64_t pos_us)
{
    // seek
    my_ffmpeg->seek(pos_us);
    // 清理 OpenAL 缓冲区和 frame_list 队列
    clear_OpenAL_buffers();
    /*for (auto it = frame_list.rbegin(); it != frame_list.rend(); ++it)
        av_frame_free(&(*it));
    frame_list.clear();*/
    // 清理 OpenAL 缓冲区和 frame_list 队列
    /*for (int i = 0; i < NUM_BUFFERS; ++i) {
        AVFrame* frame = my_ffmpeg->next_audio_out_frame();
        if (frame)
            frame_list.push_back(frame);
        else
            break;
    }*/
    // 处理OpenAL播放
    //process_playback_OpenAL();
}
